using System;
using System.Collections;
using System.Data;
using System.Globalization;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.Personnel;
using PHSRAG.Insight21.Policy;
using PHSRAG.Utility;
using PHSRAG.WebControls;
using PersonnelBO = PHSRAG.Insight21.Personnel;
using PersonnelConstants = PHSRAG.Insight21.Personnel.Constants;
using PerDetailConstants = PHSRAG.Insight21.Personnel.Constants.PersonnelDetailsConstants;

namespace PHSRAG.Insight21
{
    /// <summary>
    /// The PersonnelDetails class supports the PersonnelDetails.aspx
    /// It supports the loading of personnel details information accessible to the logged
    /// in user for a given employee job map ID and fiscal year.By default the data for the current fiscal year will be loaded.
    /// The screen also allows the user to transact on the salary adjustment and distribution data
    /// </summary>
    public partial class PersonnelDetails : NoCachePage
    {
        #region Constants

        private const string AddDistribScriptKey = "AddDistribution_752A9A35-9A96-46DC-A922-C8B038F6025C";
        private const string AllocationWarningLabelName = "allocationWarning";
        private const string AnnualSalaryLabelName = "annualSalary";
        private const string CommentPopupScriptKey = "Comments_752A9A35-9A96-46DC-A922-C8B038F6025C";
        private const string CurrentSalaryLabelName = "currentSalary";
        private const string DeleteDistributionButtonName = "deleteDistribution";
        private const string EditDistributionButtonName = "editDistribution";
        private const string EffortWarningLabelName = "effortWarning";
        private const string ExcelReportControlFilePath = "../../Personnel/Reports/PersonnelDetailsExcel.ascx";
        private const string ExpandingPanelControlName = "expandingPanel";
        private const int FirstChildControlIndex = 0;
        private const int FirstRowIndex = 0;
        private const string FiscalYearsKey = "FiscalYearsKey_752A9A35-9A96-46DC-A922-C8B038F6025C";
        private const string OnMouseOutScriptKey = "onmouseout";
        private const string OnMouseOverScriptKey = "onmouseover";
        private const string PercentEffortLabelName = "percentEffort";
        private const string SalaryDateLabelName = "salaryDate";
        private const string SplitsGridName = "splits";
        private const string TitleEditCommand = "EditTitle";
        private const string TitleEditText = "Edit";
        private const string TitleSaveCommand = "SaveTitle";
        private const string TitleSaveText = "Save";
        private const int UpdateButtionControlIndex = 3;

        #endregion

        #region Enumerations (Private)

        // Enum to list the different mode for the harvard title

        // Enum holding the column index for the fiscal year table

        #region Nested type: FiscalYearTableColumnIndex

        private enum FiscalYearTableColumnIndex
        {
            ID,
            FiscalYear
        }

        #endregion

        #region Nested type: Mode

        private enum Mode
        {
            Edit,
            Display
        }

        #endregion

        //Enum holding the salary grid column indices

        #region Nested type: SalaryGridColumns

        private enum SalaryGridColumns
        {
            AnnualRate,
            EffectiveDate,
            EditedBy,
            EditDate,
            Action
        }

        #endregion

        //Enum holding the splits grid column indices

        #region Nested type: SplitsGridColumns

        private enum SplitsGridColumns
        {
            FundNumber,
            ProjectTitle,
            AnnualRate,
            StartDate,
            EndDate,
            PercentEffort,
            ToBePaid,
            EditedBy,
            EditDate,
            Comments,
            Action
        }

        #endregion

        #endregion

        #region Instance Variables (Protected)

        protected StringBuilder errorMessages = new StringBuilder();

        #endregion

        #region Instance Variables (Private)

        private ICachePolicy cachePolicy;
        private int fiscalYearValue;
        private ICachePolicy pageCachePolicy;

        #endregion

        #region Private Properties

        /// <summary>
        /// Get the personnel details informations that the logged-in user has access to.This object is cached.
        /// </summary>
        private Personnel.PersonnelDetails PersonnelDetailsInstance
        {
            get
            {
                try
                {
                    Personnel.PersonnelDetails perDetails =
                        ResultsCache.GetObject(cachePolicy, Personnel.PersonnelDetails.CacheKey) as Personnel.PersonnelDetails;

                    if (perDetails == null)
                    {
                        perDetails = new Personnel.PersonnelDetails();
                        perDetails.Load(DataAccess.GetConnectionString()
                            , ((UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey)).DomainUserId
                            , JobMapId, FiscalYearId);
                        ResultsCache.SetObject(cachePolicy, Personnel.PersonnelDetails.CacheKey, perDetails);
                    }

                    return perDetails;
                }
                catch (Exception ex)
                {
                    throw new Exception(@"Error in loading Personnel Details", ex);
                }
            }
        }

        /// <summary>
        /// Get/Set the Employee Job Map ID for which the details information has to be retrieved.
        /// </summary>
        private int JobMapId
        {
            get
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.PersonnelDetailsPageKey) as Hashtable;
                return (h == null || h[Keys.EmployeeJobMapIDKey] == null) ? 0 : (int) h[Keys.EmployeeJobMapIDKey];
            }
            set
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.PersonnelDetailsPageKey) as Hashtable ?? new Hashtable();
                h[Keys.EmployeeJobMapIDKey] = value;
                pageCachePolicy.Cache(Keys.PersonnelDetailsPageKey, h);
            }
        }

        /// <summary>
        /// Get/Set the fiscal year ID for which the personnel details information has to be retrieved
        /// </summary>
        private int FiscalYearId
        {
            get
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.PersonnelDetailsPageKey) as Hashtable;
                return (h == null || h[Keys.FiscalYearIDKey] == null) ? 0 : (int) h[Keys.FiscalYearIDKey];
            }
            set
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.PersonnelDetailsPageKey) as Hashtable ?? new Hashtable();
                h[Keys.FiscalYearIDKey] = value;
                pageCachePolicy.Cache(Keys.PersonnelDetailsPageKey, h);
            }
        }

        #endregion

        #region Constructors

        #endregion

        #region Event Handlers

        #region Page Load

        /// <summary>
        ///  Handler for the event raised when the page is being loaded.
        ///  Retrieve the job map id and the fiscal year id and cache it using the page cache policy
        ///  Load the fiscal year list and also configure the add distribution button to open the AddDistribution pop up
        ///  Bind all the controls in the screen with the appropriate data from the PersonnelDetails instance
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnPageLoad(object sender, EventArgs e)
        {
            annunciator.Message = string.Empty;
            annunciator.Title = string.Empty;
            totalErrors.Text = string.Empty;
            distributionWarning.Text = string.Empty;
            errorMessages = new StringBuilder();

            reportDownload.SQLReportName = SSRSRequest.ReportNames.Personnel.PersonnelDetails; //"Personnel_PersonnelDetails"
            try
            {
                if (!IsPostBack)
                {
                    breadCrumb.Add("Personnel Details", Settings.
                        GetKeyValue("Other.Personnel.PersonnelDetailsURL", string.Empty),
                        Constants.DefaultDetailsPageRank);

                    Hashtable dictionary = pageCachePolicy.GetCachedObject(Keys.IncomingDataKey) as Hashtable;
                    if (dictionary != null)
                    {
                        ResultsCache.PurgeObject(cachePolicy);

                        FiscalYearId = (dictionary[Keys.FiscalYearIDKey] == null) ? 0 : (int) dictionary[Keys.FiscalYearIDKey];
                        fiscalYearValue = (dictionary[Keys.FiscalYearKey] == null) ? 0 : (int) dictionary[Keys.FiscalYearKey];
                        if (dictionary[Keys.EmployeeJobMapIDKey] != null)
                        {
                            JobMapId = (int) dictionary[Keys.EmployeeJobMapIDKey];
                            dictionary.Remove(Keys.EmployeeJobMapIDKey);
                            pageCachePolicy.Cache(Keys.IncomingDataKey, dictionary);
                        }
                    }
                    else
                    {
                        throw new Exception("Error in loading Personnel details infomation. No incoming data available for load");
                    }

                    LoadFiscalYearListAndSetID();
                    DisplayPersonnel();
                }

                DisplayDistributionDetails();

                //If any transaction has happened on the distribution, reload the related data
                object transactionflag = pageCachePolicy.GetCachedObject(Keys.TransactionFlagKey);
                if ((transactionflag == null) ? false : (bool) transactionflag)
                {
                    LoadFiscalYearListAndSetID();
                    DisplayDistributionDetails();
                    pageCachePolicy.Cache(Keys.TransactionFlagKey, false);
                }

                ConfigureAddDistributionButton();

                hiddenSearchCriteria.Value = HttpUtility.UrlEncode(string.Format("{0}|{1}", fiscalYear.SelectedItem.Text, refreshDates.SalaryAsOfDate.ToShortDateString()));
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog
                    ("Personnel Details-Load", ex, true, Utility.ExceptionReport.ReportType.Error);
                annunciator.Message = Utility.ExceptionReport.GetExceptionInfo(ex, false);
            }
        }

        #endregion

        #region Report Download

        /// <summary>
        /// Invoked by the download bar when it catches an exception.
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnDownloadFailed(object sender, CommandEventArgs e)
        {
            annunciator.Title = "Following error(s) occurred while downloading";
            annunciator.Message = Utility.ExceptionReport.GetExceptionInfo((Exception) e.CommandArgument, false);
        }

        #endregion

        #region Salary Data grid events

        /// <summary>
        /// Handler for event fired when the data grid items are created
        /// Check for the IsManagedEmployee flag and hide the action column if the employee is unmanaged.
        /// Add a button to the data grid and attach the validation script
        /// to the buttons onclick attribute if the item is an EditItem
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnItemCreated(object sender, DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.EditItem)
            {
                Button editValidation = (Button) e.Item.
                    Cells[(int) SalaryGridColumns.Action].Controls[UpdateButtionControlIndex];
                editValidation.Attributes.Add("onclick", "return ValidateSalaryAdjustment();");
            }
        }

        /// <summary>
        /// Handler for the event raised when Edit button is clicked on the salary data grid
        /// Sets the edit item index of the grid and rebind data to the grid
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnSalaryEdit(object source, DataGridCommandEventArgs e)
        {
            salaryGrid.EditItemIndex = e.Item.ItemIndex;
            salaryGrid.DataSource = PersonnelDetailsInstance.Salarys;
            salaryGrid.DataBind();
        }

        /// <summary>
        /// Handler for the event raised when Cancel button is clicked on the salary data grid
        /// Resets the edit item index of the grid and rebinds data to the grid
        /// In the case of cancel being clicked after the user tried to add a salary data,
        /// clear the PersonnelDetails object of stale salary adjustment data
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnCancel(object source, DataGridCommandEventArgs e)
        {
            int salaryItemIndex = e.Item.ItemIndex;

            Personnel.PersonnelDetails personnelDetails = PersonnelDetailsInstance;

            if (personnelDetails.Salarys[salaryItemIndex].SalaryID == 0)
            {
                personnelDetails.Salarys.RemoveSalary(salaryItemIndex);
            }

            salaryGrid.EditItemIndex = -1;
            salaryGrid.DataSource = PersonnelDetailsInstance.Salarys;
            salaryGrid.DataBind();

            addSalary.Enabled = true;
        }

        /// <summary>
        /// Handler for the event raised when the Save button is clicked on the salary data grid
        /// Add or update salary adjustment information
        /// Remove the stale PersonnelSummary instance from cache and rebind all the controls with new data
        /// This is done because once the salary is updated/added the splits change accordingly
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnSave(object source, DataGridCommandEventArgs e)
        {
            annunciator.Message = string.Empty;
            annunciator.Title = string.Empty;
            totalErrors.Text = string.Empty;
            distributionWarning.Text = string.Empty;
            errorMessages = new StringBuilder();

            salaryGrid.EditItemIndex = -1;

            DataGridItem salaryItem = e.Item;

            Salary salary = (salaryItem.ItemIndex == PersonnelDetailsInstance.Salarys.Count) ?
                                                                                                 PersonnelDetailsInstance.Salarys.CreateNewSalary() :
                                                                                                                                                        PersonnelDetailsInstance.Salarys[salaryItem.ItemIndex];

            ProcessSalary(salary, salaryItem);

            ResultsCache.PurgeObject(cachePolicy);

            LoadFiscalYearListAndSetID();
            DisplayPersonnel();
            DisplayDistributionDetails();

            addSalary.Enabled = true;
        }

        #endregion

        #region Splits data grid events

        /// <summary>
        /// Handler for the event raised when a row in the the splits grid is databound
        /// Associate Javascripts to the Edit/Delete button clicks and put the SplitId of the row in the pagecache
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnSplitsDataBound(object sender, DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
            {
                DistributionSplit split = (DistributionSplit) e.Item.DataItem;

                int splitID = split.SplitID;

                ((Button) e.Item.FindControl(EditDistributionButtonName)).Attributes["onclick"] =
                    string.Format(
                        "window.open('AddEditDistribution.aspx?{0}={1}&{2}={3}',null,'width=700,height=500,scrollbars=yes,resizable=no,status=no');"
                        , Constants.QueryStringOperationKey, Constants.UpdateOperation
                        , Constants.QueryStringSplitIDKey, splitID);

                ((Button) e.Item.FindControl(DeleteDistributionButtonName)).Attributes["onclick"] =
                    string.Format(
                        "window.open('DeleteDistribution.aspx?{0}={1}',null,'width=700,height=500,scrollbars=yes,resizable=no,status=no');"
                        , Constants.QueryStringSplitIDKey, splitID);

                //Get the comments associated with each split ID
                SalaryDistribution salaryDistribution =
                    PersonnelDetailsInstance.SalaryDistribs.GetSalaryDistribution(splitID);

                string comments = salaryDistribution.Comment;

                if (!comments.Equals(string.Empty))
                {
                    ImageButton commentsLink = new ImageButton();
                    commentsLink.ImageUrl = VirtualPathUtility.ToAbsolute("~/Images/Note.gif");

                    //escape any apostrophe's so we don't get javascript errors on the client.
                    string formattedComments = comments.Replace("'", "\\'");
                    //Do the replace to remove the any super fluous escape sequences in the string
                    string note = string.Format("notes={0}", formattedComments.Replace("\r\n", string.Empty));
                    commentsLink.Attributes.Add("onclick", "return ShowComments('Comments.aspx','" + note + "')");
                    e.Item.Cells[(int) SplitsGridColumns.Comments].Controls.Add(commentsLink);

                    ((ImageButton) e.Item.Cells[(int) SplitsGridColumns.Comments].Controls[1]).ToolTip = comments;
                }

                string fundNumberToolTipText =
                    string.Format("Project Start Date: {3}{0}Project End Date: {4}{0}Budget Start Date: {1}{0}Budget End Date: {2}{0}Employee Status: {5}{0}Sponsor Number: {6}{0}Sponsor Name: {7}{0}Fund Category: {8}{0}"
                        , "\n"
                        , split.BudgetStartDate
                        , split.BudgetEndDate
                        , split.ProjectStartDate
                        , split.ProjectEndDate
                        , PersonnelDetailsInstance.Status
                        , split.SponsorNumber
                        , split.SponsorName
                        , split.FundCategory);

                e.Item.Cells[0].ToolTip = fundNumberToolTipText;
            }
            else if (e.Item.ItemType == ListItemType.Footer)
            {
                DistributionSplits distSplits = (DistributionSplits) ((DataGrid) sender).DataSource;
                decimal totalAnnualRate = 0, totalCurrentSalary = 0;
                decimal totalPercentEffort = 0;

                foreach (DistributionSplit split in distSplits)
                {
                    totalAnnualRate += split.AnnualRate;
                    totalCurrentSalary += split.ToBePaid;
                    totalPercentEffort += split.PercentEffort;
                }

                e.Item.Cells[(int) SplitsGridColumns.FundNumber].Text = "Total";
                e.Item.Cells[(int) SplitsGridColumns.AnnualRate].Text = string.Format("{0:c}", totalAnnualRate);
                e.Item.Cells[(int) SplitsGridColumns.PercentEffort].Text = string.Format("{0:0.}%", totalPercentEffort);
                e.Item.Cells[(int) SplitsGridColumns.ToBePaid].Text = string.Format("{0:c}", totalCurrentSalary);
            }
        }

        #endregion

        #region Repeater Events

        /// <summary>
        /// Handler for the event raised when a row in the repeater is databound.
        /// Databind all the controls present within the repeater
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnPeriodsDataBound(object sender, RepeaterItemEventArgs e)
        {
            totalErrors.Enabled = true;
            RepeaterItem distribPeriodItem = e.Item;

            if (distribPeriodItem.ItemType == ListItemType.Item ||
                distribPeriodItem.ItemType == ListItemType.AlternatingItem)
            {
                DistributionPeriod distribPeriod = (DistributionPeriod) distribPeriodItem.DataItem;

                ExpandingPanel expandingPanel = (ExpandingPanel) distribPeriodItem.FindControl(ExpandingPanelControlName);

                ((Label) expandingPanel.FindControl(SalaryDateLabelName)).Text =
                    distribPeriod.EffectiveDate;
                ((Label) expandingPanel.FindControl(CurrentSalaryLabelName)).Text =
                    string.Format("{0:c}", distribPeriod.CurrentSalary);

                if (distribPeriod.Splits.Count == 0)
                {
                    ((Label) expandingPanel.FindControl(AllocationWarningLabelName)).Text =
                        string.Format("* No distributions exist for period {0} - {1} *"
                            , distribPeriod.EffectiveDate, distribPeriod.ExpirationDate);

                    errorMessages.AppendFormat("No distributions exist for period {0} - {1}"
                        , distribPeriod.EffectiveDate, distribPeriod.ExpirationDate);

                    expandingPanel.IsExpanded = true;
                }
                else
                {
                    ((Label) expandingPanel.FindControl(AllocationWarningLabelName)).Text =
                        distribPeriod.AllocationWarning;

                    if (distribPeriod.AllocationWarning != "")
                    {
                        errorMessages.AppendFormat("{0}<br>", distribPeriod.AllocationWarning);
                    }

                    ((Label) expandingPanel.FindControl(EffortWarningLabelName)).Text =
                        distribPeriod.EffortWarning;

                    if (distribPeriod.EffortWarning != "")
                    {
                        errorMessages.AppendFormat("{0}<br>", distribPeriod.EffortWarning);
                    }

                    DataGrid splits = (DataGrid) expandingPanel.FindControl(SplitsGridName);

                    //If the employee is unmanaged disable editing and deleting
                    if (!PersonnelDetailsInstance.IsManagedEmployee)
                    {
                        splits.Columns[(int) SplitsGridColumns.Action].Visible = false;
                    }

                    splits.DataSource = distribPeriod.Splits;
                    splits.DataBind();
                    expandingPanel.IsExpanded = true;
                }

                if (errorMessages.Length > 0)
                {
                    totalErrors.Text = string.Format("{0}{1}", totalErrors.Text == string.Empty ? string.Empty : "<br>", errorMessages);
                }
            }
        }

        #endregion

        #region DropdownList index changed Event

        /// <summary>
        /// Handler for the event raised when the the selected value is changed in the fiscal year drop down
        /// Update the new fiscal year value in the cache using the page cache policy
        /// Remove the personnel summary instance from cache and rebind all the control with new data
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnFiscalYearChanged(object sender, EventArgs e)
        {
            annunciator.Message = string.Empty;
            annunciator.Title = string.Empty;
            totalErrors.Text = string.Empty;
            distributionWarning.Text = string.Empty;
            errorMessages = new StringBuilder();

            FiscalYearId = Convert.ToInt32(fiscalYear.SelectedValue);
            hiddenSearchCriteria.Value = HttpUtility.UrlEncode(string.Format("{0}|{1}", fiscalYear.SelectedItem.Text, refreshDates.SalaryAsOfDate.ToShortDateString()));

            ResultsCache.PurgeObject(cachePolicy);
            DisplayPersonnel();
            DisplayDistributionDetails();
        }

        #endregion

        #region Button Click Events

        /// <summary>
        /// Handler for the event raised when the Harvard title edit button is clicked
        /// If the command text is save, update the data
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnTitleEdit(object sender, EventArgs e)
        {
            if (editTitle.CommandName.Equals(TitleEditCommand))
            {
                ToggleMode(Mode.Edit);
            }
            else if (editTitle.CommandName.Equals(TitleSaveCommand))
            {
                try
                {
                    PersonnelDetailsInstance.HarvardTitle = harvardTitleEntered.Text;
                    PersonnelDetailsInstance.UpdateHarvardTitle
                        (DataAccess.ConnectionString
                            , ((UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey)).DomainUserId
                            , JobMapId);

                    harvardTitle.Text = harvardTitleEntered.Text;
                    ToggleMode(Mode.Display);
                }
                catch (Exception ex)
                {
                    Utility.ExceptionReport.WriteToEventLog
                        ("Personnel Details-Harvard Title update", ex, true, Utility.ExceptionReport.ReportType.Error);
                    annunciator.Message = Utility.ExceptionReport.GetExceptionInfo(ex, false);
                }
            }
        }

        /// <summary>
        /// Handler for event raised when the Cancel button is clicked for harvard title edit operation
        /// Set the harvard title display style and changes the buttons that are displayed
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnTitleEditCancel(object sender, EventArgs e)
        {
            harvardTitleEntered.Text = harvardTitle.Text;
            ToggleMode(Mode.Display);
        }

        /// <summary>
        /// Handler for event raised when the Update Comment button is clicked
        /// Set the default edit data and edit by values and update the data
        /// </summary>
        /// <param name="sender">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnUpdateComment(object sender, EventArgs e)
        {
            PersonnelDetailsInstance.Comment = comment.Text;
            PersonnelDetailsInstance.CommentEditDate = DateTime.Now.ToShortDateString();
            PersonnelDetailsInstance.CommentEditedBy = ((UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey)).Username;
            try
            {
                PersonnelDetailsInstance.SaveComments
                    (DataAccess.GetConnectionString()
                        , ((UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey)).DomainUserId
                        , FiscalYearId
                        , JobMapId);
                noteEditedBy.Text = PersonnelDetailsInstance.CommentEditedBy;
                noteEditDate.Text = PersonnelDetailsInstance.CommentEditDate;
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog
                    ("Personnel Details-Comment update", ex, true, Utility.ExceptionReport.ReportType.Error);
                annunciator.Message = Utility.ExceptionReport.GetExceptionInfo(ex, false);
            }
        }

        /// <summary>
        /// Handler for event raised when the Add Salary button is clicked
        /// Set the defualt values for the new salary object and rebind data to the salary grid
        /// </summary>
        /// <param name="source">Control which raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnAddSalary(object source, EventArgs e)
        {
            Salary salary = PersonnelDetailsInstance.Salarys.CreateNewSalary();
            // default the salary audit information
            salary.EmployeeJobMapID = JobMapId;
            salary.EditDate = DateTime.Now.ToShortDateString();
            salary.EditedBy = ((UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey)).Username;
            salaryGrid.EditItemIndex = PersonnelDetailsInstance.Salarys.Count - 1;
            salaryGrid.DataSource = PersonnelDetailsInstance.Salarys;
            salaryGrid.DataBind();

            addSalary.Enabled = false;
        }

        #endregion

        #endregion

        #region Private methods

        /// <summary>
        /// Data binds all the controls on the screen with appropriate
        /// data from the personnel details instance
        /// </summary>
        private void DisplayPersonnel()
        {
            Personnel.PersonnelDetails personnelDetails = PersonnelDetailsInstance;

            name.Text = personnelDetails.EmployeeName;
            jobCode.Text = personnelDetails.JobCode;
            hrDepartment.Text = personnelDetails.HRDepartment;
            status.Text = personnelDetails.Status;
            jobTitle.Text = personnelDetails.Title;
            harvardTitle.Text = personnelDetails.HarvardTitle;
            harvardTitleEntered.Text = personnelDetails.HarvardTitle;
            employeeId.Text = personnelDetails.EmployeeID;
            recordNumber.Text = personnelDetails.RecordNumber;

            salaryAsOfDate.Text = refreshDates.SalaryAsOfDate.ToShortDateString();

            //If the employee is unmanaged hide all the buttons and disable the comment text box
            if (!personnelDetails.IsManagedEmployee)
            {
                updateComment.Visible = editTitle.Visible = addSalary.Visible =
                    addDistribution.Visible = commentGrid.Visible = false;

                salaryGrid.Columns[(int) SalaryGridColumns.Action].Visible = false;
            }
            else
            {
                comment.Text = personnelDetails.Comment;
                noteEditedBy.Text = personnelDetails.CommentEditedBy;
                noteEditDate.Text = personnelDetails.CommentEditDate;
            }

            salaryGrid.DataSource = personnelDetails.Salarys;
            salaryGrid.DataBind();
        }

        /// <summary>
        /// Data binds the repeater control to display the Distribution details
        /// </summary>
        private void DisplayDistributionDetails()
        {
            try
            {
                DistributionPeriods distribPeriods = PersonnelDetailsInstance.DistribPeriods;

                if (distribPeriods.Count > 0)
                {
                    periods.DataSource = PersonnelDetailsInstance.DistribPeriods;
                    periods.DataBind();
                    periods.Visible = true;
                }
                else
                {
                    periods.Visible = false;
                    distributionWarning.Text = "* No distributions exist, salary requirements are not met *";
                }
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.WriteToEventLog
                    ("Personnel Details-Distribution-Load", ex, true, Utility.ExceptionReport.ReportType.Error);
                annunciator.Message = Utility.ExceptionReport.GetExceptionInfo(ex, false);
            }
        }

        /// <summary>
        /// Process the salary information depending on the operation Add/Update
        /// </summary>
        /// <param name="salary">salary object</param>
        /// <param name="salaryItem">A row from the salary adjustment grid</param>
        private void ProcessSalary(Salary salary, DataGridItem salaryItem)
        {
            //Remove the $ symbol from the text before setting the value
            salary.AnnualRate = Convert.ToDecimal
                (((TextBox) salaryItem.
                    Controls[(int) SalaryGridColumns.AnnualRate].
                    Controls[FirstChildControlIndex]).Text.
                    Replace(CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol, string.Empty));

            salary.EffectiveDate = ((TextBox) salaryItem.
                Controls[(int) SalaryGridColumns.EffectiveDate].
                Controls[FirstChildControlIndex]).Text;

            salary.EditDate = DateTime.Now.ToShortDateString();

            UserContext userContext = (UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey);
            salary.EditedBy = userContext.Username;
            try
            {
                // check for an update or add based on whether the salaryid is zero
                if (salary.SalaryID > 0)
                {
                    salary.UpdateSalary(DataAccess.GetConnectionString(), userContext.DomainUserId);
                }
                else
                {
                    salary.AddSalary(DataAccess.GetConnectionString(), userContext.DomainUserId);
                }
            }
            catch (Exception ex)
            {
                Utility.ExceptionReport.
                    WriteToEventLog("Personnel Details", ex, true, Utility.ExceptionReport.ReportType.Error);
                annunciator.Message = Utility.ExceptionReport.GetExceptionInfo(ex, false);
            }
        }

        /// <summary>
        /// Toggle the display mode for the controls involved in the edit harvard title
        /// </summary>
        /// <param name="mode">The mode for which the display has to be set</param>
        private void ToggleMode(Mode mode)
        {
            switch (mode)
            {
                case (Mode.Display):
                    editTitle.Text = TitleEditText;
                    editTitle.CommandName = TitleEditCommand;
                    harvardTitle.Visible = true;
                    harvardTitleEntered.Visible = false;
                    cancelTitleEdit.Visible = false;
                    break;
                case (Mode.Edit):
                    editTitle.Text = TitleSaveText;
                    editTitle.CommandName = TitleSaveCommand;
                    harvardTitle.Visible = false;
                    harvardTitleEntered.Visible = true;
                    cancelTitleEdit.Visible = true;
                    break;
            }
        }

        /// <summary>
        /// Load the fiscal years for which the user has data and set the selected value for the fiscal year drop down
        /// This method is used for the initial load and also for further manipulations as transactions happen
        /// <remarks>
        /// Following is the list of different scenarios for the fiscal year load and the resultant logic used
        /// <list type="bullet">
        /// <item><description>Invoke the sproc to load the fiscal years for the given JobMapID</description></item>
        /// <item><description>Bind the data table to the fiscalYear dropdown list</description></item>
        /// <item><description>Check if the calling page has provided the FiscalYearId 
        /// or FiscalYearId has already been populated</description></item>
        /// <item><description>If the FiscalYearId has already been populated and if it has been deleted 
        /// set the "SelectedIndex" of the ddl & FiscalYearID private property to the latest 
        /// fiscal year in the list loaded from the DB</description></item>
        /// <item><description>If yes, set the "SelectedIndex" of the ddl with the FiscalYearID provided</description></item>
        /// <item><description>If no, set the "SelectedIndex" of the ddl & FiscalYearID private property 
        /// with the ID of the current fiscal year provided the user has data for the current fiscal year.
        /// If the user does not have data for the current fiscal year, set the "SelectedIndex" of the ddl & 
        /// FiscalYearID private property to the latest fiscal year in the list loaded from the DB</description></item>
        /// <item><description>If the calling page has provided the fiscalYearValue set the "SelectedIndex" of the ddl & 
        /// FiscalYearID private property to the ID of the given fiscal year</description></item>
        /// </list>
        /// </remarks> 
        /// </summary>
        private void LoadFiscalYearListAndSetID()
        {
            DataTable fiscalYears = Personnel.PersonnelDetails.GetFiscalYearList
                (((UserContext) cachePolicy.GetCachedObject(UserContext.CacheKey)).DomainUserId
                    , JobMapId);

            fiscalYear.DataSource = fiscalYears;
            fiscalYear.DataValueField = fiscalYears.Columns[(int) FiscalYearTableColumnIndex.ID].ColumnName;
            fiscalYear.DataTextField = fiscalYears.Columns[(int) FiscalYearTableColumnIndex.FiscalYear].ColumnName;
            fiscalYear.DataBind();

            DataRow[] fiscalYearRows;

            if (FiscalYearId != 0)
            {
                ListItem fiscalYearItem = fiscalYear.Items.FindByValue(FiscalYearId.ToString());
                if (fiscalYearItem != null)
                {
                    fiscalYear.SelectedIndex = fiscalYear.Items.IndexOf
                        (fiscalYear.Items.FindByValue(FiscalYearId.ToString()));
                }
                    //If the active FiscalYearID has been deleted
                else
                {
                    SetLastFiscalYear(fiscalYears);
                }
            }
                //If the FiscalYearId has not been provided by the calling page
                //set it to the current fiscal year's ID 
            else
            {
                fiscalYearRows =
                    fiscalYears.Select(string.Format("FiscalYear = {0}", ((DateTime.Now.Month > 9) ? 1 : 0) + DateTime.Now.Year));
                if (fiscalYearRows.Length > 0)
                {
                    SetFiscalYear(fiscalYearRows[FirstRowIndex]);
                }
                    //If the user does not have data for the current fiscal year 
                    //set the fiscal year to the latest in the list loaded
                else
                {
                    SetLastFiscalYear(fiscalYears);
                }
            }
            //If the calling page sends the FiscalYear set the FiscalYearID value accordingly
            if (fiscalYearValue != 0)
            {
                fiscalYearRows =
                    fiscalYears.Select(string.Format("FiscalYear = {0}", fiscalYearValue));
                if (fiscalYearRows.Length > 0)
                {
                    SetFiscalYear(fiscalYearRows[FirstRowIndex]);
                }
            }
        }

        /// <summary>
        /// Set the FiscalYearID to the selected ID and
        /// set the SelectedIndex of the fiscalYear ddl appropriatly
        /// </summary>
        /// <param name="row">DataRow with FiscaYear and the associated id</param>
        private void SetFiscalYear(DataRow row)
        {
            int id = (int) row[(int) FiscalYearTableColumnIndex.ID];
            FiscalYearId = id;
            fiscalYear.SelectedIndex = fiscalYear.Items.
                IndexOf(fiscalYear.Items.FindByValue(id.ToString()));
        }

        /// <summary>
        /// Set the FiscalYearID to the id of the last available year in the list
        /// and set the SelectedIndex of the fiscalYear ddl appropriatly
        /// </summary>
        /// <param name="fiscalYearTable"></param>
        /// ---------------------------------------------------------------------
        /// Patch for the Exception Error "There is no row at position -1".
        /// This exception occurs whenever fiscalYearTable has no records (data problem).
        /// The added module assigns current year to the drop-down years list.
        /// This will not be needed after the feeds will be fixed for that.
        /// The issue is described in TrackIt 7274 but applies also to the Active
        /// records, not only Terminated. 11/01/2007  JML.
        /// ---------------------------------------------------------------------
        private void SetLastFiscalYear(DataTable fiscalYearTable)
        {
            int lastIndex = fiscalYearTable.Rows.Count - 1;
            if (lastIndex != -1)
            {
                FiscalYearId = Convert.ToInt32(fiscalYearTable.Rows[lastIndex][(int) FiscalYearTableColumnIndex.ID]);
                fiscalYear.SelectedIndex = lastIndex;
            }
            else
            {
                DateTime currentDay = DateTime.Now;
                string currentYear = currentDay.Year.ToString();
                fiscalYear.Items.Add(currentYear);
                fiscalYear.SelectedIndex = 0;
            }
        }

        /// <summary>
        /// Set the javascript to launch the add distribution popup
        /// </summary>
        private void ConfigureAddDistributionButton()
        {
            Page.ClientScript.RegisterStartupScript(GetType(), AddDistribScriptKey
                , string.Format(
                    "<script language=\"javascript\">\n" +
                        "function OpenPopup(){0}" +
                            "window.open('AddEditDistribution.aspx?{1}={2}&{3}={4}',null,'width=700,height=500,menubar=no,resizable=no,scrollbars=yes');\n" +
                                "{5}</script>\n"
                    , "{", Constants.QueryStringOperationKey, Constants.AddOperation
                    , Constants.QueryStringJobMapIDKey, JobMapId, "}"));

            addDistribution.Attributes["onclick"] = "OpenPopup()";
        }

        #endregion

        #region Web Form Designer generated code

        /// <summary>
        /// Handler for the event raised when the page is being initialized.
        /// Retrieve the cache policy and the page cache policy in force.
        /// </summary>
        /// <param name="e">Argument associated with the event</param>
        protected override void OnInit(EventArgs e)
        {
            InitializeComponent();
            base.OnInit(e);

            cachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
            pageCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.PageCachePolicyKey];

            this.reportDownload.FailedAction += new CommandEventHandler(OnDownloadFailed);
        }

        /// <summary>
        /// Required method for Designer support
        /// </summary>
        private void InitializeComponent()
        {
            this.Load += new System.EventHandler(this.OnPageLoad);
        }

        #endregion

        public void PurgeCache()
        {
            ResultsCache.PurgeObject(cachePolicy);
        }

        protected void OnHeaderNavigate(object sender, CommandEventArgs e)
        {
            PurgeCache();
        }

        protected void OnBreadCrumbNavigate(object sender, CommandEventArgs e)
        {
            PurgeCache();
        }
    }
}